﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "BurnDlgHsan.h"
#include <QNetworkInterface>
#include <QFileDialog>
#include <QMessageBox>
#include <QSettings>
#include <QProcess>
#include "BurnCtrl/BurnCtrlFaction.h"
#include "DataConvertor.h"

namespace BurnDlg {

namespace {
    const int COLUMN_WIDTH_SIZE_20 = 20;
    const int COLUMN_WIDTH_SIZE_100 = 100;
    const int COLUMN_WIDTH_SIZE_200 = 200;
    const int COLUMN_MINIMUM_HEIGHT_SIZE_192 = 192;
    const int IP_BYTE_NUMBER_4 = 4;
    const int MAX_ONE_BYTE_255 = 255;
    const int LAST_IP_BYTE_NUMBER_2 = 2;
    const int BLOCK_COUNT_NUMBER_1000 = 1000;
    const char* const STRING_START = "Start";
    const char* const STRING_STOP = "Stop";
}

BurnDlgHsan::BurnDlgHsan(ChipType chipType, QWidget *pParent)
    : BurnDlg(pParent),
      ui(new Ui::BurnDlgHsan),
      ipConfigSetting(new IPConfigSetting()),
      actChangeChip(new QAction(stringClass.GetChangeChip())),
      actAbout(new QAction(stringClass.GetAbout())),
      isEmptyFlash(false),
      eraseConfigSetting(new EraseConfigSetting())
{
    if (ui == nullptr) {
        return;
    }
    this->chipType = chipType;
    ui->setupUi(this);
    SetUiCbTypeItem(ui);
    BurnDlgHsan::InitBurnCtrl();
    if (burnCtrl != nullptr) {
        burnCtrl->StartWiteMonitoring();
    }
    binModel = new BinFileModel();
    ui->menuOption->addAction(actChangeChip);
    ui->menuHelp->addAction(actAbout);
    ui->menuSetting->addMenu(menuWifiLanguage);
    if (actChangeChip != nullptr) {
        connect(actChangeChip, &QAction::triggered, this, &BurnDlgHsan::SlotShowChangeChip);
    }
    if (actAbout != nullptr) {
        connect(actAbout, &QAction::triggered, this, &BurnDlgHsan::SlotShowAbout);
    }
    ui->textEditReceive->setReadOnly(true);
    ui->textEditReceive->document()->setMaximumBlockCount(BLOCK_COUNT_NUMBER_1000);
    ui->lineEditSeleteFile->setFocusPolicy(Qt::NoFocus);
    ui->progressBarReceive->hide();  // 进度条暂时隐藏掉

    QSettings cfg(Global::GetAppConfigPath() + "\\config_" +
                  Global::ChipTypeIdToString(Global::currentChipTypeId) + "\\config.ini",
                  QSettings::IniFormat);
    ui->lePid->setText(cfg.value("USB_CONFIG/PID").toString());
    ui->leVid->setText(cfg.value("USB_CONFIG/VID").toString());
    SlotCbnSetfocusComboCom();
    SlotGetIpAddressList();
    InitTableView();
    InitMenus();
    InitConnectSignal();
    ui->cbEmptyFlash->setCheckState(Qt::Checked);
    if (chipType == ChipType::HSANEMEI) {
        ui->cbEmptyFlash->setCheckState(Qt::Unchecked);
        ui->cbEmptyFlash->setEnabled(false);
    }
    isEmptyFlash = ui->cbEmptyFlash->checkState() == Qt::Checked ? true : false;
}

BurnDlgHsan::~BurnDlgHsan()
{
}

bool BurnDlgHsan::SetUiCbTypeItem(Ui::BurnDlgHsan* uiNeedSet) const
{
    if (uiNeedSet == nullptr) {
        return false;
    }

    uiNeedSet->cbType->addItem("Serial");
    if (this->chipType == ChipType::HSANLUOFU || this->chipType == ChipType::HSANEMEI ||
        this->chipType == ChipType::HSANXILING || this->chipType == ChipType::HSANTIANGONG0) {
        uiNeedSet->lbType->hide();
        uiNeedSet->cbType->hide();
        uiNeedSet->lbVid->hide();
        uiNeedSet->lbPid->hide();
        uiNeedSet->lePid->hide();
        uiNeedSet->leVid->hide();
        uiNeedSet->hsUsb->changeSize(0, 0);
    } else {
        uiNeedSet->cbType->addItem("USB");
    }

    return true;
}

void BurnDlgHsan::SlotCbnSetfocusComboCom() const
{
    if (ui == nullptr) {
        return;
    }
    if (portType == Base::TCP_PORT) {
        return;
    }
    QString strPort = ui->cbCom->currentText();
    ui->cbCom->clear();
    QStringList strListPort;
    DataConvertor::RefreshComList(strListPort);
    ui->cbCom->addItems(strListPort);
    for (int i = 0; i < strListPort.size(); i++) {
        if (strListPort.at(i) == strPort) {
            ui->cbCom->setCurrentText(strPort);
        }
    }
}

void BurnDlgHsan::SlotGetIpAddressList()
{
    if (ui == nullptr) {
        return;
    }
    ui->cbIpAddr->clear();
    QStringList addrList;
    QList<QHostAddress> hostAddressList = QNetworkInterface::allAddresses();
    if (!hostAddressList.isEmpty()) {
        for (int i = 0; i < hostAddressList.count(); i++) {
            if (hostAddressList[i].protocol() == QAbstractSocket::IPv4Protocol) {
                addrList.push_back(hostAddressList[i].toString());
            }
        }
    }
    ui->cbIpAddr->addItems(addrList);

    for (int i = 0; i < addrList.size(); i++) {
        IPInfo info;
        QString str = "";
        info.subnetMask = "255.255.254.0";

        QStringList list = addrList[i].split(".");
        if (list.size() != IP_BYTE_NUMBER_4) {
            return;
        }
        for (int j = 0; j < list.size() - 1; j++) {
            str += list[j] + ".";
        }
        info.gateway = str + "1";
        int ipFinal = list[3].toInt() + 1;
        if (ipFinal == MAX_ONE_BYTE_255) {
            ipFinal = LAST_IP_BYTE_NUMBER_2;
        }
        info.ip = str + QString::number(ipFinal);
        mapIPInfo.insert(addrList[i], info);
    }
}

void BurnDlgHsan::SlotBtnClickIPConfig(bool checked)
{
    Q_UNUSED(checked)
    QString ip = ui->cbIpAddr->currentText();
    ipConfigSetting->SetIPAddress(mapIPInfo[ip].ip, mapIPInfo[ip].subnetMask, mapIPInfo[ip].gateway);
    ipConfigSetting->show();
}

void BurnDlgHsan::SlotBtnClickEraseConfig(bool checked) const
{
    Q_UNUSED(checked)
    eraseConfigSetting->show();
}

void BurnDlgHsan::InitConnectSignal()
{
    connect(ui->cbCom, &ComboBoxCom::Clicked, this, &BurnDlgHsan::SlotCbnSetfocusComboCom);
    connect(ui->cbIpAddr, &ComboBoxCom::Clicked, this, &BurnDlgHsan::SlotGetIpAddressList);
    connect(ui->pbIPConfig, &QPushButton::clicked, this, &BurnDlgHsan::SlotBtnClickIPConfig);
    connect(ui->btnSelectFile, &QPushButton::clicked, this, &BurnDlgHsan::SlotBnClickedButtonSelectfile);
    connect(ui->btnStart, &QPushButton::clicked, this, &BurnDlgHsan::SlotBnClickedButtonStartburn);
    connect(ipConfigSetting, &IPConfigSetting::SignalSaveAddress, this, &BurnDlgHsan::SlotSaveAddress);
    connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked, this,
            &BurnDlgHsan::SlotBnClickedTableHeader);
    connect(ui->tableView, &QTableView::clicked, this, &BurnDlgHsan::SlotBnClickedTableView);
    connect(ui->cbEmptyFlash, &QCheckBox::stateChanged, this, &BurnDlgHsan::SlotEmptyFlashStateChanged);
    connect(ui->pbEraseConfig, &QPushButton::clicked, this, &BurnDlgHsan::SlotBtnClickEraseConfig);

    connect(ui->cbType, SIGNAL(currentIndexChanged(const QString &)), this,
            SLOT(SlotCurrentIndexChangedType(const QString &)));

    connect(ui->lePid, SIGNAL(textEdited(const QString &)), this,
            SLOT(SlotVidPidTextEdited(const QString &)));
    connect(ui->leVid, SIGNAL(textEdited(const QString &)), this,
            SLOT(SlotVidPidTextEdited(const QString &)));

    connect(burnCtrl, &BurnCtrl::SignalInsertPlainText, this, &BurnDlgHsan::SlotInsertPlainText);
    connect(burnCtrl, &BurnCtrl::SignalBurnFileFailed, this, &BurnDlgHsan::SlotBurnFileFailed);
    connect(burnCtrl, &BurnCtrl::SignalBurnFileFinish, this, &BurnDlgHsan::SlotBurnFileFinish);

    connect(eraseConfigSetting, &EraseConfigSetting::SignalSetEraseConfig, this, &BurnDlgHsan::SlotSetEraseConfig);
}

void BurnDlgHsan::SlotVidPidTextEdited(const QString &str) const
{
    Q_UNUSED(str);
    QSettings cfg(Global::GetAppConfigPath() + "\\config_" +
                  Global::ChipTypeIdToString(Global::currentChipTypeId) + "\\config.ini",
                  QSettings::IniFormat);
    cfg.setValue("USB_CONFIG/PID", ui->lePid->text());
    cfg.setValue("USB_CONFIG/VID", ui->leVid->text());
}

void BurnDlgHsan::SlotCurrentIndexChangedType(const QString &str)
{
    isEmptyFlash = (ui->cbEmptyFlash->checkState() == Qt::Checked) ? true : false;
    QMap<int, BIN_INFO> binInfo = binModel->GetMapBinFileInfo();
    QMap<int, bool> mapCheck = binModel->GetMapCheck();

    for (auto it = binInfo.begin(); it != binInfo.end(); it++) {
        if (str == "Serial") {
            if (it->type == IMAGE_TYPE::IMAGE_TYPE_ESBC_FASTBOOT ||
                it->type == IMAGE_TYPE::IMAGE_TYPE_ALL_FASTBOOT) {
                mapCheck[it.key()] = false;
            }

            if ((it->type == IMAGE_TYPE::IMAGE_TYPE_DDR) && (isEmptyFlash)) {
                mapCheck[it.key()] = true;
            } else if ((it->type == IMAGE_TYPE::IMAGE_TYPE_DDR) && (!isEmptyFlash)) {
                mapCheck[it.key()] = false;
            }
        } else if (str == "USB") {
            if (it->type == IMAGE_TYPE::IMAGE_TYPE_DDR ||
                it->type == IMAGE_TYPE::IMAGE_TYPE_UBOOT_ORIGIN) {
                mapCheck[it.key()] = false;
            }

            if ((it->type == IMAGE_TYPE::IMAGE_TYPE_ESBC_FASTBOOT ||
                it->type == IMAGE_TYPE::IMAGE_TYPE_ALL_FASTBOOT) && (!isEmptyFlash)) {
                mapCheck[it.key()] = false;
            }
        }
    }

    binModel->FetchData(mapCheck);
}

void BurnDlgHsan::SlotSaveAddress(QString address, QString subnetMask, QString gateway)
{
    QString ip = ui->cbIpAddr->currentText();
    mapIPInfo[ip].ip = address;
    mapIPInfo[ip].subnetMask = subnetMask;
    mapIPInfo[ip].gateway = gateway;
}

void BurnDlgHsan::SlotBnClickedButtonSelectfile()
{
    QString filePath = QFileDialog::getOpenFileName(this, tr("Select File"), "", stringClass.GetIdsString32941());
    if (!filePath.isEmpty()) {
        AnalyseBinFile(filePath);
    }
    SlotEmptyFlashStateChanged(true);
    SlotCurrentIndexChangedType(ui->cbType->currentText());
}

void BurnDlgHsan::AnalyseBinFile(QString filePath)
{
    if (burnCtrl == nullptr) {
        QMessageBox::critical(this, stringClass.GetMessageError(),
                              stringClass.GetIdsString32984(), stringClass.GetYes());
        return;
    }
    if (ui == nullptr || binModel == nullptr) {
        return;
    }
    ui->lineEditSeleteFile->setText(filePath);
    QMap<int, bool> mapLastBinCheck = binModel->GetMapCheck();
    QMap<int, BIN_INFO> mapLastBinInfo = binModel->GetMapBinFileInfo();
    binModel->ClearData();
    BIN_INFO_ARRAY infoArray;
    QString version;
    QMap<int, BIN_INFO> mapBinInfo = binModel->GetMapBinFileInfo();
    DataConvertor::AnalysMultifBin(ui->lineEditSeleteFile->text(), infoArray, version,
                                   isEncrypt, encryptBuffer);
    for (int i = 0; i < infoArray.GetSize(); i++) {
        if (infoArray.GetAt(i).type == IMAGE_TYPE::IMAGE_TYPE_SMALL_SSB ||
            infoArray.GetAt(i).type == IMAGE_TYPE::IMAGE_TYPE_SMALL_SSB_SHA) {
            continue;
        }
        mapBinInfo[mapBinInfo.size()] = infoArray.GetAt(i);
    }
    QMap<int, bool> mapCheck;
    if (!isCheckAble) {
        AnalyseBinFileSub(mapBinInfo, mapCheck, mapLastBinInfo, mapLastBinCheck);
    } else {
        for (int i = 0; i < mapBinInfo.size(); i++) {
            mapCheck[i] = true;
        }
    }
    binModel->FetchData(mapCheck);
    binModel->FetchData(mapBinInfo);
}

void BurnDlgHsan::InitTableView() const
{
    if (binModel == nullptr) {
        return;
    }
    binModel->SetEditableCheckbox(isCheckAble);
    if (ui != nullptr) {
        ui->tableView->setModel(binModel);
        ui->tableView->setDragEnabled(true);
        ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
        ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
        ui->tableView->setDefaultDropAction(Qt::MoveAction);
        ui->tableView->setDragDropMode(QAbstractItemView::InternalMove);
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_CHECK, COLUMN_WIDTH_SIZE_20);
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_ADD, COLUMN_WIDTH_SIZE_20);
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_DELETE, COLUMN_WIDTH_SIZE_20);
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_NAME, COLUMN_WIDTH_SIZE_100);
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_PAHT, COLUMN_WIDTH_SIZE_200);
        ui->tableView->setMinimumHeight(COLUMN_MINIMUM_HEIGHT_SIZE_192);
        ui->tableView->horizontalHeader()->setSectionResizeMode(TABLEVIEW_COLUMN_CHECK, QHeaderView::Fixed);
        ui->tableView->horizontalHeader()->setSectionResizeMode(TABLEVIEW_COLUMN_ADD, QHeaderView::Fixed);
        ui->tableView->horizontalHeader()->setSectionResizeMode(TABLEVIEW_COLUMN_DELETE, QHeaderView::Fixed);
    }
}

void BurnDlgHsan::InitMenus()
{
    this->tableViewMenu = new QMenu(this);
    QAction *createNewFileAction = new QAction(stringClass.GetCreateNewFile());
    if (createNewFileAction != nullptr) {
        this->tableViewMenu->addAction(createNewFileAction);
        connect(createNewFileAction, &QAction::triggered, this, &BurnDlgHsan::SlotCreateNewFwpkgFile);
    }
    QAction *saveSpiltFileAction = new QAction(stringClass.GetSaveSplitFile());
    if (saveSpiltFileAction != nullptr) {
        this->tableViewMenu->addAction(saveSpiltFileAction);
        connect(saveSpiltFileAction, &QAction::triggered, this, &BurnDlgHsan::SlotSaveSpiltFwpkgFile);
    }
}

void BurnDlgHsan::contextMenuEvent(QContextMenuEvent *event)
{
    Q_UNUSED(event);
    if (!ui->tableView->selectionModel()->currentIndex().isValid()) {
        return;
    }
    this->tableViewMenu->exec(QCursor::pos());
}

void BurnDlgHsan::SlotCreateNewFwpkgFile()
{
    CreateNewFwpkgFile(ui->lineEditSeleteFile->text());
}

void BurnDlgHsan::SlotSaveSpiltFwpkgFile()
{
    SaveSpiltFwpkgFile(ui->lineEditSeleteFile->text());
}

void BurnDlgHsan::AnalyseBinFileSub(QMap<int, BIN_INFO> &mapBinInfo, QMap<int, bool> &mapCheck,
                                    QMap<int, BIN_INFO> &mapLastBinInfo,
                                    QMap<int, bool> &mapLastBinCheck) const
{
    for (int i = 0; i < mapBinInfo.size(); i++) {
        int j = 0;
        for (; j < mapLastBinInfo.size(); j++) {
            if (mapBinInfo[i].fileName.compare(mapLastBinInfo[j].fileName) == 0) {
                mapCheck[i] = mapLastBinCheck[j];
                break;
            }
        }
        if (j == mapLastBinInfo.size()) {
            mapCheck[i] = true;
        }
    }
}

void BurnDlgHsan::InitBurnCtrl()
{
    BurnCtrlFaction faction;
    burnCtrl = faction.GetBurnCtrl(chipType);
    if (burnCtrl != nullptr) {
        burnCtrl->SetBurnDlg(this);
    }
}

void BurnDlgHsan::HandleWhenUiTtnStartIsStart()
{
    ui->textEditReceive->clear();
    UpdateList();
    ui->progressBarReceive->setValue(0);
    burnCtrl->SetIsCancel(FALSE);
    SlotInsertPlainText(stringClass.GetIdsString32897());
    int i = 0;
    QMap<int, BIN_INFO> binInfo = binModel->GetMapBinFileInfo();
    QMap<int, BIN_INFO> checkBinInfo;
    QMap<int, bool> binCheck = binModel->GetMapCheck();
    for (auto it = binInfo.begin(); it != binInfo.end(); it++) {
        if (binCheck[it.key()]) {
            checkBinInfo[i++] = it.value();
        }
    }
    if (checkBinInfo.size() == 0 || (!QFile(ui->lineEditSeleteFile->text()).exists())) {
        QMessageBox::warning(nullptr, MESSAGE_BURN_TOOL, stringClass.GetNoValidFlashFile());
        ui->btnStart->setText(STRING_START);
        return;
    }
    SlotCurrentIndexChangedType(ui->cbType->currentText());
    burnCtrl->InitBurnCmdByArray(checkBinInfo);
    burnCtrl->SetIsEmptyFlash(isEmptyFlash);
    ui->btnStart->setText(STRING_STOP);
    emit SignalSendMsgInBurnDlg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_STARTCONNECT), 0, 0);
}

void BurnDlgHsan::SlotBnClickedButtonStartburn()
{
    if (burnCtrl == nullptr || ui == nullptr || binModel == nullptr) {
        return;
    }

    if (!ConfigComPort()) {
        return;
    }

    if (WhetherProssRunning("tftpd32.exe")) {
        QMessageBox::critical(this, tr("error"),
                              tr("It is detected that tftpd32.exe is running. Close it."), QMessageBox::Yes);
        return;
    }

    HidDeviceInfo deviceInfo;
    bool ok = false;
    deviceInfo.pid = ui->lePid->text().toUInt(&ok, 0x10);
    deviceInfo.vid = ui->leVid->text().toUInt(&ok, 0x10);
    burnCtrl->SetUsbDeviceInfo(deviceInfo);
    IPInfo info;
    QString ip = ui->cbIpAddr->currentText();
    info = mapIPInfo[ip];
    burnCtrl->SetBoardIPAddress(info);
    burnCtrl->SetIPAddress(ip);
    burnCtrl->SetHSANTypeFlag(ui->cbType->currentText());
    if (ui->btnStart->text() == STRING_START) {
        HandleWhenUiTtnStartIsStart();
    } else {
        burnCtrl->SetIsCancel(TRUE);
        burnCtrl->BurnCtrlCancel();
    }
}

bool BurnDlgHsan::WhetherProssRunning(const QString &processName) const
{
    QProcess process;
    QString cmd = "tasklist";
    process.start(cmd);
    process.waitForFinished();
    QString str = process.readAllStandardOutput();
    if (str.contains(processName)) {
        return true;
    } else {
        return false;
    }
}

bool BurnDlgHsan::ConfigComPort()
{
    if (ui == nullptr) {
        return false;
    }
    QString strPort = ui->cbCom->currentText();
    if (ui->cbCom->findText(strPort) != -1) {
        ui->cbCom->setCurrentText(strPort);
    } else {
        return false;
    }
    if (ui->cbCom->count() == 0) {
        QMessageBox::warning(this, "", stringClass.GetIdsString32899());
        return false;
    } else {
        int com = strPort.mid(3).toInt();
        if (burnCtrl != nullptr) {
            burnCtrl->SetComNum(com);
            return true;
        }
        return false;
    }
}

void BurnDlgHsan::SlotInsertPlainText(const QString &editStr)
{
    if (ui == nullptr) {
        return;
    }
    ui->textEditReceive->moveCursor(QTextCursor::End);
    ui->textEditReceive->insertPlainText(editStr);
    ui->textEditReceive->ensureCursorVisible();
}

void BurnDlgHsan::SlotBurnFileFailed()
{
    if (ui == nullptr) {
        return;
    }
    ui->btnStart->setText(STRING_START);
    SlotInsertPlainText(IDS_STRING_32879 + "\n");
    SlotInsertPlainText(stringClass.GetIdsString32881());
    SlotInsertPlainText(IDS_STRING_32879 + "\n");
    ui->progressBarReceive->setValue(0);
}

void BurnDlgHsan::SlotBurnFileFinish()
{
    if (ui == nullptr) {
        return;
    }
    ui->btnStart->setText(STRING_START);
    SlotInsertPlainText(IDS_STRING_32879 + "\n");
    SlotInsertPlainText(stringClass.GetIdsString32880() + "\n");
    SlotInsertPlainText(IDS_STRING_32879 + "\n");
}

void BurnDlgHsan::SlotSetEraseConfig(unsigned int cbEraseConfig,
    const QVector<EraseConfig> &vecEraseConfig) const
{
    burnCtrl->SetEraseConfig(cbEraseConfig, vecEraseConfig);
}

void BurnDlgHsan::SlotEmptyFlashStateChanged(int state)
{
    Q_UNUSED(state)
    SlotCurrentIndexChangedType(ui->cbType->currentText());
}

}
